/*
 * Created on 2008-10-31
 *
 * TODO
 */
package jacky.lanlan.song.test.net;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * HTTP连接器。
 * 
 * @author Jacky.Song
 */
public class HttpConnector {

	private String host;
	private int port = -1;

	public HttpConnector(String host) {
		this.host = host;
	}
	
	public HttpConnector(String host, int port) {
		this.host = host;
		this.port = port;
	}
	
	public URL makeURL(String url) {
		StringBuilder req = new StringBuilder("http://").append(host);
		if (port != -1) {
			req.append(":").append(port);
		}
		req.append(url);
		try {
			URL u = new URL(req.toString());
			return u;
		}
		catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public HttpURLConnection connect(String url, boolean get) {
		try {
			URL u = makeURL(url);
			HttpURLConnection con = (HttpURLConnection) u.openConnection();
			con.setRequestMethod(get ? "GET" : "POST");
			return con;
		}
		catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	// 产生查询字符串
	private String geneQueryString(Map<String, String> queryParams) {
		if (queryParams != null) {
			StringBuilder paramString = new StringBuilder('?');
			try {
				for (String key : queryParams.keySet()) {
					paramString.append(key).append('=').append(URLEncoder.encode(queryParams.get(key), "iso-8859-1"));
					paramString.append('&');
				}
				paramString.deleteCharAt(paramString.length() - 1);
			}
			catch (Exception e) {
			}
			return paramString.toString();
		}
		return "";
	}
	
	/**
	 * 发起GET请求，并接收响应。
	 * 
	 * @param url
	 *          请求url
	 * @param queryParams
	 *          查询参数
	 * @param verbose
	 *          是否产生冗余输出
	 * @return 服务器响应的文本形式
	 */
	public String doGet(final String path, final Map<String, String> queryParams, final boolean verbose) {
		return visit(verbose, new Callable<HttpURLConnection>() {
			@Override
			public HttpURLConnection call() throws Exception {
				return connect(path + geneQueryString(queryParams), true);
			}
		});
	}
	
	/**
	 * 发起GET请求，然后自己处理HTTP连接。
	 * 
	 * @param url
	 *          请求url
	 * @param queryParams
	 *          查询参数
	 * @param handler
	 *          处理HTTP连接，包括请求前设置和接收响应
	 */
	public void doGet(String path, Map<String, String> queryParams, HttpConnectionHandler handler) {
		try {
			handler.process(connect(path + this.geneQueryString(queryParams), true));
		}
		catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	/**
	 * 发起POST请求，并接收响应。
	 * 
	 * @param url
	 *          请求url
	 * @param queryParams
	 *          查询参数
	 * @param verbose
	 *          是否产生冗余输出
	 * @return 服务器响应的文本形式
	 */
	public String doPost(final String path, final Map<String, String> queryParams, final boolean verbose) {
		return visit(verbose, new Callable<HttpURLConnection>() {
			@Override
			public HttpURLConnection call() throws Exception {
				HttpURLConnection connection = connect(path + geneQueryString(queryParams), false);
				connection.getOutputStream().write(geneQueryString(queryParams).getBytes());// Post的参数设置在请求体中
				return connection;
			}
		});
	}
	
	/**
	 * 发起POST请求，然后自己处理HTTP连接。
	 * 
	 * @param url
	 *          请求url
	 * @param queryParams
	 *          查询参数
	 * @param handler
	 *          处理HTTP连接，包括请求前设置和接收响应
	 */
	public void doPost(final String path, final Map<String, String> queryParams, HttpConnectionHandler handler) {
		try {
			HttpURLConnection connection = connect(path, false);
			connection.getOutputStream().write(geneQueryString(queryParams).getBytes());
			handler.process(connection);
		}
		catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	/**
	 * 访问指定url，返回响应。
	 * 
	 * @param url
	 *          想连接的url
	 * @param get
	 *          是否是HTTP GET方法
	 * @param verbose
	 *          是否产生冗余数据
	 * @return 服务器响应的文本形式
	 */
	private String visit(boolean verbose, Callable<HttpURLConnection> conFactory) {
		StringWriter resp = new StringWriter();
		PrintWriter pw = new PrintWriter(resp);

		try {
			HttpURLConnection conn = conFactory.call();// connect(url, get);
			conn.connect();

			int code = conn.getResponseCode();
			if (verbose) {
				pw.println("resp : " + code + " " + conn.getResponseMessage());
			}

			InputStream in = null;
			if (code == HttpURLConnection.HTTP_OK) {
				in = conn.getInputStream();
			}
			else {
				return resp.toString();
			}

			if (verbose) {
				pw.println("response info:");
			}
			
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			try {
				String line = null;
				while ((line = br.readLine()) != null) {
					pw.println(line);
				}
			}
			finally {
				in.close();
				br.close();
			}

			return resp.toString();
		}
		catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	/**
	 * 启动数个线程并发连接指定URL。
	 * 
	 * @param urls
	 *          要访问的URL列表，随机选取访问
	 * @param get
	 *          是否是HTTP GET方法
	 * @param concuNum
	 *          并发连接数，即启动多少个线程访问urls
	 * @param handler
	 *          处理HTTP连接，包括请求前设置和接收响应
	 */
	public void concurrentAccess(final String[] urls, final boolean get, int concuNum, final HttpConnectionHandler handler) {
		final CountDownLatch latch = new CountDownLatch(1);
		Thread[] testers = new Thread[concuNum];

		final AtomicInteger counter = new AtomicInteger(0);
		for (int i = 0; i < testers.length; i++) {
			testers[i] = new Thread((new Runnable() {
				@Override
				public void run() {
					counter.incrementAndGet();
					try {
						latch.await();
						handler.process(connect(urls[(int) (Math.random() * urls.length)], get));
					}
					catch (Exception e) {
						throw new RuntimeException(e);
					}
				}
			}));
			testers[i].start();
		}

		while (counter.get() < concuNum)
			;// 等待所有线程都启动
		
		latch.countDown();// 开始并发连接

		try {
			for (Thread tester : testers) {
				tester.join();
			}
		}
		catch (Exception e) {
		}
		
	}

	/**
	 * 连接指定URL，返回服务器响应。
	 * 
	 * @param host
	 *          域名
	 * @param port
	 *          端口
	 * @param path
	 *          路径
	 * @param queryParams
	 *          查询参数
	 * @return 服务器响应
	 */
	public static String doGet(String host, int port, String path, Map<String, String> queryParams) {
		return new HttpConnector(host, port).doGet(path, queryParams, false);
	}
	
	
	private ExecutorService pool = null;
	
	/**
	 * 启动数个线程并发连接指定URL列表，使用线程池。
	 * 
	 * @param urls
	 *          要访问的URL列表，随机选取访问
	 * @param get
	 *          是否是HTTP GET方法
	 * @param connNum
	 *          访问次数
	 * @param handler
	 *          处理HTTP连接，包括请求前设置和接收响应
	 */
	public void poolingConcurrentAccess(final String[] urls, final boolean get, int conNum,
			final HttpConnectionHandler handler) {
		int poolSize = (conNum / 10);
		pool = Executors.newFixedThreadPool(poolSize <= 1 ? 1 : poolSize);

		final AtomicInteger count = new AtomicInteger();
		for (int i = 0; i < conNum; i++) {
			pool.execute((new Runnable() {
				@Override
				public void run() {
					try {
						handler.process(connect(urls[(int) (Math.random() * urls.length)], get));
					}
					catch (Exception e) {
						throw new RuntimeException(e);
					}
					finally {
						count.incrementAndGet();
					}
				}
			}));
		}

		while (count.get() != conNum)
			; // 阻塞，直到所有任务完成
		
	}
}
